﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;
using System.IO;
using System.Text.RegularExpressions;
using System.Security.Permissions;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Forms;

using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Inventory;
using Styx.CommonBot.POI;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

using UltraFTW.data.libs.core;

namespace UltraFTW.data.libs.util
{
    static class FTWProps
    {
        public static string ClassAndSpec = null;
        public static Dictionary<String, FakeCooldown> fakecooldowns = null;
        public static Dictionary<string, DateTime> spellWasLastCast = new Dictionary<string, DateTime>();

        public static string data_path;
        public static string rules_path;
        public static string class_rules_path;
        public static string gen_rules_path;

        public static DateTime nextrest = DateTime.Now;
        public static bool UseScrollLock = true;

        public static Dictionary<String, String> rules = new Dictionary<String, String>();
        public static Dictionary<String, Dictionary<String, Dictionary<String, List<List<FTWCondition>>>>> rule_defs = new Dictionary<String, Dictionary<String, Dictionary<String, List<List<FTWCondition>>>>>();
        public static Dictionary<String, FTWAction> action_index = new Dictionary<String, FTWAction>();
        public static FTWCore core;
        public static bool firstpulse = true;

        public static HashSet<String> AOESpells;
        public static HashSet<String> Stuns;
        public static HashSet<String> TankOnlySpells;
        public static HashSet<String> CastOver;
        public static HashSet<String> IgnoreMobsBattleground;
        public static HashSet<String> IgnoreCanCast;
        public static Dictionary<String, int> MobWeights;
        public static string redtext = "";

        public static string mode = string.Empty;
        public static Dictionary<string, int> fakePetCooldownTimes = new Dictionary<string, int>
                {
                    {"Leap", 10},
                    {"Gnaw", 10}
                };
        public static WoWUnit tank;
        public static WoWUnit healer;
        public static List<WoWPlayer> people = new List<WoWPlayer>();
        public static List<WoWPlayer> closePeople = new List<WoWPlayer>();
        public static WoWPlayer healTarget;
        public static WoWPlayer cleanseTarget;
        public static WoWPlayer reviveTarget;
        public static WoWUnit add;
        public static int avgHealth = 100;
        public static int _eclipsedirection = 1;
        public static string lastspellcastname = string.Empty;
        public static DateTime lastspellcasttime = DateTime.Now;
        public static Dictionary<string, DateTime> CastOnce = new Dictionary<string, DateTime>();
        public static int CastOnceTime = 120;
        public static List<WoWUnit> adds = new List<WoWUnit>();
        public static List<WoWUnit> nontargetadds = new List<WoWUnit>();
        public static HashSet<WoWDispelType> dispels = new HashSet<WoWDispelType>();
        public static int cliptime = 50;
        public static double range = 4.95;
        public static int not_in_los_attempts = 0;
        public static DateTime MovementCooldown = DateTime.Now;
        public static HashSet<String> manaspellnames = new HashSet<String> { "Restore Mana", "Water Spirit" };
        public static HashSet<String> healthspellnames = new HashSet<String> { "Healing Potion", "Healthstone" };
        public static HashSet<String> debugitems = new HashSet<String> { };

        static FTWProps()
        {
            data_path = Styx.CommonBot.Routines.RoutineManager.RoutineDirectory + "\\UltraFTW\\data";
            rules_path = data_path + "\\rules";
            class_rules_path = rules_path + "\\classes";
            gen_rules_path = rules_path + "\\general";

            if (!File.Exists(gen_rules_path + "\\Spell_Overrides.txt"))
            {
                string s = string.Format("Could not find path:\n\n{0}\n\nMake sure you have installed this software to the following path:\n\n    HonorBuddy\\Routines\\UltraFTWised", data_path);
                MessageBox.Show(s, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                throw new Exception(s);
            }
        }

        public static void LoadProps()
        {
            try
            {
                rule_defs = new Dictionary<String, Dictionary<String, Dictionary<String, List<List<FTWCondition>>>>>();
                action_index = new Dictionary<String, FTWAction>();
                ClassAndSpec = StyxWoW.Me.ClassAndSpec();
                LoadRules();
                LoadDispels();
                core = new FTWCore();
            }
            catch (Exception ex)
            {
                FTWLogger.debug(Colors.Red,ex.Message);
            }
        }

        private static void CompileRules()
        {
            foreach (string cls in FTWProps.rules.Keys)
            {
                Dictionary<String, Dictionary<String, List<List<FTWCondition>>>> class_rule_def = new Dictionary<String, Dictionary<String, List<List<FTWCondition>>>>();

                List<String> lines = FTWProps.rules[cls].Split('\n').ToList();

                for (int i = 0; i < lines.Count; i++)
                    lines[i] = lines[i].Replace("\n", "").Replace("\r", "");

                string parse_line_adv = @"(?<target>.+)\.(?<aura_action>.+)\(""(?<aura>.*)""\)\s+(?<operator>[^\s]+)\s+(?<value>[^\s]+)";
                Regex cond_reg_adv = new Regex(parse_line_adv);

                string parse_line_basic = @"(?<target>.+)\.(?<prop>.+)\s+(?<operator>[^\s]+)\s+(?<value>[^\s]+)";
                Regex cond_reg_basic = new Regex(parse_line_basic);

                string parse_line_prop = @"(?<target>.+)\.(?<prop>.+)\s+(?<value>.+)";
                Regex cond_reg_prop = new Regex(parse_line_prop);

                string section = "";
                string action = "";
                for (int i = 0; i < lines.Count; i++)
                {
                    // Skip blank lines
                    string tmp_line = lines[i].Trim();
                    if (!(lines[i].Replace("\t", "").Trim().Length == 0 || lines[i].Trim().StartsWith("--")))
                    {
                        if (lines[i].StartsWith("@"))
                        {
                            section = tmp_line;
                            if (!class_rule_def.ContainsKey(section))
                                class_rule_def.Add(section, new Dictionary<String, List<List<FTWCondition>>>());
                        }
                        else if (!lines[i].StartsWith("\t"))
                        {
                            if (section == "")
                                FTWLogger.debug(Colors.Red, "Action being set outside a section");
                            else
                            {
                                action = FTWUtils.cleanString(tmp_line);
                                FTWAction ftw_act = new FTWAction();
                                ftw_act.raw_str = action;

                                string[] a_segs = action.Split(' ');
                                if (!a_segs[0].Contains('.'))
                                {
                                    ftw_act.target_str = "Target";
                                    ftw_act.action = "Cast";
                                    ftw_act.param = action;                                    
                                }
                                else
                                {
                                    if (a_segs.Length > 1)
                                    {
                                        string[] a2_segs = a_segs[0].Split('.');
                                        ftw_act.target_str = a2_segs[0];
                                        ftw_act.action = a2_segs[1];
                                        ftw_act.param = action.Substring(a_segs[0].Length + 1);
                                    }
                                    else
                                        FTWLogger.debug(Colors.Red, "Invalid action {0}",action);
                                }

                                FTWCoreActions.compileActionFunction(ftw_act);

                                if (!FTWProps.action_index.ContainsKey(action))
                                    FTWProps.action_index.Add(action, ftw_act);
                                if (!class_rule_def[section].ContainsKey(action))
                                    class_rule_def[section].Add(action, new List<List<FTWCondition>>());
                                class_rule_def[section][action].Add(new List<FTWCondition>());
                            }
                        }
                        else
                        {
                            if (section == "")
                                FTWLogger.debug(Colors.Red, "Condition being set outside a section");
                            else if (action == "")
                                FTWLogger.debug(Colors.Red, "Condition being set outside an action");
                            else
                            {
                                Match adv_match = cond_reg_adv.Match(tmp_line);
                                if (adv_match.Success)
                                {
                                    FTWCondition cond = new FTWCondition();
                                    cond.type = "ADVANCED";
                                    cond.target_str = adv_match.Groups["target"].Value.Trim();
                                    cond.aura_action = adv_match.Groups["aura_action"].Value;
                                    cond.aura = adv_match.Groups["aura"].Value;
                                    cond.op = adv_match.Groups["operator"].Value;
                                    try
                                    {
                                        cond.value = System.Double.Parse(adv_match.Groups["value"].Value.Trim());
                                        cond.raw_str = tmp_line;
                                        class_rule_def[section][action].ElementAt(class_rule_def[section][action].Count - 1).Add(cond);
                                    }
                                    catch (Exception ex)
                                    {
                                        FTWLogger.debug(Colors.Red, "Invalid value = " + adv_match.Groups["value"].Value.Trim());
                                    }
                                }
                                else
                                {
                                    Match basic_match = cond_reg_basic.Match(tmp_line);
                                    if (basic_match.Success)
                                    {
                                        FTWCondition cond = new FTWCondition();
                                        cond.type = "BASIC";
                                        cond.target_str = basic_match.Groups["target"].Value.Trim();
                                        cond.prop = basic_match.Groups["prop"].Value;
                                        cond.op = basic_match.Groups["operator"].Value;
                                        try
                                        {
                                            cond.value = System.Double.Parse(basic_match.Groups["value"].Value.Trim());
                                            cond.raw_str = tmp_line;
                                            class_rule_def[section][action].ElementAt(class_rule_def[section][action].Count - 1).Add(cond);
                                        }
                                        catch (Exception ex)
                                        {
                                            FTWLogger.debug(Colors.Red, "Invalid value = " + basic_match.Groups["value"].Value.Trim());
                                            FTWLogger.debug(Colors.Red, "    " + cond.ToString());
                                        }                                            
                                    }
                                    else
                                        FTWLogger.log("UNKNOWN ACTION = " + lines[i]);
                                }                                
                            }
                        }
                    }
                }
                foreach (string sect in class_rule_def.Keys)
                {
                    FTWLogger.log("SECTION = " + sect);
                    foreach (string a_str in class_rule_def[sect].Keys)
                    {
                        FTWLogger.log("    ACTION = " + a_str);
                        foreach (List<FTWCondition> cond_blk in class_rule_def[sect][a_str])
                        {
                            FTWLogger.log("       BLOCK-------------");
                            foreach (FTWCondition cond in cond_blk)
                            {
                                FTWLogger.log("          " + cond.ToString());
                            }
                        }
                    }
                }
                rule_defs.Add(cls,class_rule_def);
            }
        }

        private static void LoadRules()
        {
            fakecooldowns = LoadFakeCooldowns(gen_rules_path + "\\Spell_Overrides.txt");
            AOESpells = LoadList(gen_rules_path + "\\Spell_Target_Circle.txt");
            Stuns = LoadList(gen_rules_path + "\\Spell_Stuns.txt");
            TankOnlySpells = LoadList(gen_rules_path + "\\Spell_Tank_Only.txt");
            CastOver = LoadList(gen_rules_path + "\\Spell_Cast_Over.txt");
            IgnoreMobsBattleground = LoadList(gen_rules_path + "\\Ignore_Mobs_Battleground.txt");
            IgnoreCanCast = LoadList(gen_rules_path + "\\Spell_Ignore_CanCast.txt");
            MobWeights = LoadWeights(gen_rules_path + "\\Mob_Weights.txt");
            try
            {
                FTWLogger.log("Loading rules for {0}", ClassAndSpec);
                string filename = FTWProps.class_rules_path + "\\" + ClassAndSpec + ".txt";
                rules[ClassAndSpec] = File.ReadAllText(filename);
                CompileRules();
            }
            catch (Exception ex)
            {
                FTWLogger.log(Colors.Red, "Error loading data file for {0}! {1}", ClassAndSpec, ex.Message);
            }
        }

        private static Dictionary<String, FakeCooldown> LoadFakeCooldowns(String filename)
        {
            string[] readText = File.ReadAllLines(filename);
            Dictionary<String, FakeCooldown> lst = new Dictionary<String, FakeCooldown>();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < readText.Length; i++)
            {
                string line = readText[i];
                if (line.StartsWith("//") || line.Length < 5)
                    continue;
                FakeCooldown fc = new FakeCooldown(line);
                sb.AppendLine(String.Format("    {0}\t{1}\t{2}", fc.SpellID, fc.Cooldown, fc.Name));
                if (lst.ContainsKey(fc.Name))
                    throw new Exception(string.Format("Spell {0} entered in {1} twice!", fc.Name, filename));
                lst.Add(fc.Name, fc);
            }
            return lst;
        }

        private static HashSet<String> LoadList(String filename)
        {
            HashSet<String> h = new HashSet<String>();
            string[] lines = File.ReadAllLines(filename);
            foreach (String line in lines)
            {
                String s = line.Trim();
                if (s.Length == 0 || s.StartsWith("//") || s.StartsWith("--"))
                    continue;
                if (h.Contains(s))
                    throw new Exception(string.Format("Line '{0}' entered in file '{1}' twice!", s, filename));
                h.Add(s);
            }
            return h;
        }

        private static Dictionary<String, int> LoadWeights(String filename)
        {
            Dictionary<String, int> h = new Dictionary<String, int>();
            string[] lines = File.ReadAllLines(filename);
            foreach (String line in lines)
            {
                String s = line.Trim();
                if (s.Length == 0 || s.StartsWith("//") || s.StartsWith("--"))
                    continue;
                String[] words = s.Split('\t');
                if (h.ContainsKey(words[1]))
                    throw new Exception(string.Format("Line '{0}' entered in file '{1}' twice!", s, filename));
                h[words[1]] = Int32.Parse(words[0]);
            }
            return h;
        }

        private static void LoadDispels()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                if (SpellManager.HasSpell("Detox"))
                {
                    // Monk
                    if (!FTWProps.dispels.Contains(WoWDispelType.Disease)) FTWProps.dispels.Add(WoWDispelType.Disease);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Poison)) FTWProps.dispels.Add(WoWDispelType.Poison);
                }
                if (SpellManager.HasSpell("Remove Corruption"))
                {
                    // Druid
                    if (!FTWProps.dispels.Contains(WoWDispelType.Curse)) FTWProps.dispels.Add(WoWDispelType.Curse);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Poison)) FTWProps.dispels.Add(WoWDispelType.Poison);
                }
                if (SpellManager.HasSpell("Remove Curse"))
                {
                    // Mage
                    if (!FTWProps.dispels.Contains(WoWDispelType.Curse)) FTWProps.dispels.Add(WoWDispelType.Curse);
                }
                if (SpellManager.HasSpell("Purify Spirit"))
                {
                    // Shaman
                    if (!FTWProps.dispels.Contains(WoWDispelType.Curse)) FTWProps.dispels.Add(WoWDispelType.Curse);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Magic)) FTWProps.dispels.Add(WoWDispelType.Magic);
                }
                if (SpellManager.HasSpell("Cleanse"))
                {
                    // Paladin
                    if (!FTWProps.dispels.Contains(WoWDispelType.Disease)) FTWProps.dispels.Add(WoWDispelType.Disease);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Poison)) FTWProps.dispels.Add(WoWDispelType.Poison);
                }
                if (SpellManager.HasSpell("Mass Dispel"))
                {
                    // Priest
                    if (!FTWProps.dispels.Contains(WoWDispelType.Curse)) FTWProps.dispels.Add(WoWDispelType.Curse);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Disease)) FTWProps.dispels.Add(WoWDispelType.Disease);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Magic)) FTWProps.dispels.Add(WoWDispelType.Magic);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Poison)) FTWProps.dispels.Add(WoWDispelType.Poison);
                }
                if (SpellManager.HasSpell("Purify"))
                {
                    // Priest
                    if (!FTWProps.dispels.Contains(WoWDispelType.Disease)) FTWProps.dispels.Add(WoWDispelType.Disease);
                    if (!FTWProps.dispels.Contains(WoWDispelType.Magic)) FTWProps.dispels.Add(WoWDispelType.Magic);
                }
                if (SpellManager.HasSpell("Sacred Cleansing") ||
                    SpellManager.HasSpell("Nature's Cure") ||
                    SpellManager.HasSpell("Improved Cleanse Spirit"))
                {
                    if (!FTWProps.dispels.Contains(WoWDispelType.Magic)) FTWProps.dispels.Add(WoWDispelType.Magic);
                }
                if (FTWProps.dispels.Count > 0)
                {
                    FTWLogger.log("Player can dispel the following:");
                    foreach (WoWDispelType d in FTWProps.dispels)
                        FTWLogger.log("    {0}", d.ToString());
                }
            }
        }
    }
}
